<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Universal Debug Panel</title>
    <style>
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }

      body {
        font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        background-color: #1e1e1e;
        color: #d4d4d4;
        height: 100vh;
        overflow: hidden;
      }

      .debug-panel {
        display: flex;
        flex-direction: column;
        height: 100vh;
      }

      /* 顶部工具栏 */
      .toolbar {
        background-color: #2d2d30;
        border-bottom: 1px solid #464647;
        padding: 8px 16px;
        display: flex;
        align-items: center;
        justify-content: space-between;
        flex-shrink: 0;
      }

      .status-indicator {
        width: 8px;
        height: 8px;
        border-radius: 50%;
        display: inline-block;
        margin-right: 8px;
      }

      .status-indicator.connected {
        background-color: #4caf50;
      }

      .status-indicator.disconnected {
        background-color: #f44336;
      }

      .status-text {
        font-size: 12px;
        color: #cccccc;
      }

      .toolbar-controls {
        display: flex;
        gap: 12px;
        align-items: center;
      }

      .toolbar button {
        background: #0e639c;
        border: none;
        color: white;
        padding: 4px 12px;
        border-radius: 3px;
        cursor: pointer;
        font-size: 12px;
      }

      .toolbar button:hover {
        background: #1177bb;
      }

      /* Tab页签 */
      .tabs {
        background-color: #2d2d30;
        border-bottom: 1px solid #464647;
        display: flex;
        flex-shrink: 0;
      }

      .tab {
        padding: 8px 16px;
        cursor: pointer;
        border-right: 1px solid #464647;
        background-color: #37373d;
        color: #cccccc;
        font-size: 13px;
        position: relative;
      }

      .tab:hover {
        background-color: #3c3c3c;
      }

      .tab.active {
        background-color: #1e1e1e;
        color: #ffffff;
      }

      .tab.active::after {
        content: '';
        position: absolute;
        bottom: 0;
        left: 0;
        right: 0;
        height: 2px;
        background-color: #007acc;
      }

      /* 筛选栏 */
      .filter-bar {
        background-color: #252526;
        border-bottom: 1px solid #464647;
        padding: 8px 16px;
        display: flex;
        gap: 12px;
        align-items: center;
        flex-shrink: 0;
      }

      .filter-input {
        background: #3c3c3c;
        border: 1px solid #464647;
        color: #d4d4d4;
        padding: 4px 8px;
        border-radius: 3px;
        font-size: 12px;
        flex: 1;
      }

      .level-filters {
        display: flex;
        gap: 8px;
      }

      .level-filter {
        padding: 3px 8px;
        border-radius: 3px;
        cursor: pointer;
        font-size: 11px;
        border: 1px solid transparent;
      }

      .level-filter.debug {
        background: #564b8d;
      }
      .level-filter.info {
        background: #0c5aa6;
      }
      .level-filter.log {
        background: #16825d;
      }
      .level-filter.warn {
        background: #b7741e;
      }
      .level-filter.error {
        background: #bc3f3c;
      }

      .level-filter.active {
        border-color: #ffffff;
        box-shadow: 0 0 0 1px #ffffff;
      }

      /* 内容区域 */
      .content {
        flex: 1;
        overflow: hidden;
        display: flex;
        flex-direction: column;
      }

      .content-panel {
        display: none;
        flex: 1;
        overflow: auto;
        padding: 16px;
      }

      .content-panel.active {
        display: block;
      }

      /* 日志样式 */
      .log-entry {
        margin-bottom: 8px;
        padding: 6px 12px;
        border-radius: 3px;
        border-left: 3px solid transparent;
        font-family: 'Consolas', 'Monaco', monospace;
        font-size: 12px;
        line-height: 1.4;
        word-break: break-word;
      }

      .log-entry.debug {
        background: rgba(86, 75, 141, 0.1);
        border-left-color: #564b8d;
      }

      .log-entry.info {
        background: rgba(12, 90, 166, 0.1);
        border-left-color: #0c5aa6;
      }

      .log-entry.log {
        background: rgba(22, 130, 93, 0.1);
        border-left-color: #16825d;
      }

      .log-entry.warn {
        background: rgba(183, 116, 30, 0.1);
        border-left-color: #b7741e;
      }

      .log-entry.error {
        background: rgba(188, 63, 60, 0.1);
        border-left-color: #bc3f3c;
      }

      .log-timestamp {
        color: #6a6a6a;
        margin-right: 8px;
      }

      .log-level {
        color: #ffffff;
        background: rgba(255, 255, 255, 0.1);
        padding: 1px 4px;
        border-radius: 2px;
        margin-right: 8px;
        font-size: 10px;
        text-transform: uppercase;
      }

      .log-message {
        color: #d4d4d4;
      }

      /* 网络请求样式 */
      .network-entry {
        margin-bottom: 8px;
        padding: 8px 12px;
        border-radius: 3px;
        background: rgba(124, 58, 237, 0.1);
        border-left: 3px solid #7c3aed;
        font-size: 12px;
        cursor: pointer;
      }

      .network-entry:hover {
        background: rgba(124, 58, 237, 0.2);
      }

      .network-header {
        display: flex;
        justify-content: space-between;
        align-items: center;
        margin-bottom: 4px;
      }

      .network-method {
        color: #ffffff;
        font-weight: bold;
        margin-right: 8px;
      }

      .network-method.GET {
        color: #4caf50;
      }
      .network-method.POST {
        color: #ff9800;
      }
      .network-method.PUT {
        color: #2196f3;
      }
      .network-method.DELETE {
        color: #f44336;
      }

      .network-url {
        color: #d4d4d4;
        flex: 1;
        text-overflow: ellipsis;
        overflow: hidden;
        white-space: nowrap;
      }

      .network-status {
        margin-left: 8px;
        padding: 2px 6px;
        border-radius: 2px;
        font-size: 10px;
      }

      .network-status.success {
        background: #4caf50;
        color: white;
      }

      .network-status.error {
        background: #f44336;
        color: white;
      }

      .network-duration {
        color: #6a6a6a;
        font-size: 11px;
      }

      .network-details {
        display: none;
        margin-top: 8px;
        padding-top: 8px;
        border-top: 1px solid #464647;
      }

      .network-details.show {
        display: block;
      }

      .network-section {
        margin-bottom: 12px;
      }

      .network-section-title {
        color: #ffffff;
        font-weight: bold;
        margin-bottom: 4px;
        font-size: 11px;
        text-transform: uppercase;
      }

      .network-section-content {
        background: #2d2d30;
        padding: 8px;
        border-radius: 3px;
        font-family: 'Consolas', 'Monaco', monospace;
        white-space: pre-wrap;
        font-size: 11px;
        max-height: 200px;
        overflow-y: auto;
      }

      .empty-state {
        text-align: center;
        color: #6a6a6a;
        margin-top: 40px;
        font-size: 14px;
      }

      /* 统计信息 */
      .stats {
        position: fixed;
        bottom: 16px;
        right: 16px;
        background: rgba(45, 45, 48, 0.9);
        padding: 8px 12px;
        border-radius: 4px;
        font-size: 11px;
        color: #cccccc;
        border: 1px solid #464647;
      }

      /* 滚动条样式 */
      ::-webkit-scrollbar {
        width: 8px;
      }

      ::-webkit-scrollbar-track {
        background: #2d2d30;
      }

      ::-webkit-scrollbar-thumb {
        background: #464647;
        border-radius: 4px;
      }

      ::-webkit-scrollbar-thumb:hover {
        background: #6a6a6a;
      }
    </style>
  </head>
  <body>
    <div class="debug-panel">
      <!-- 顶部工具栏 -->
      <div class="toolbar">
        <div class="status-container">
          <span id="status_indicator" class="status-indicator disconnected"></span>
          <span id="status" class="status-text">Disconnected</span>
        </div>
        <div class="toolbar-controls">
          <label>
            <input type="checkbox" id="autoScroll" checked />
            Auto Scroll
          </label>
          <button id="clearBtn">Clear</button>
          <button id="exportBtn">Export</button>
        </div>
      </div>

      <!-- Tab页签 -->
      <div class="tabs">
        <div class="tab active" data-tab="console">Console</div>
        <div class="tab" data-tab="network">Network</div>
      </div>

      <!-- 筛选栏 -->
      <div class="filter-bar">
        <input type="text" class="filter-input" id="searchInput" placeholder="Filter messages..." />
        <div class="level-filters">
          <div class="level-filter debug active" data-level="debug">DEBUG</div>
          <div class="level-filter info active" data-level="info">INFO</div>
          <div class="level-filter log active" data-level="log">LOG</div>
          <div class="level-filter warn active" data-level="warn">WARN</div>
          <div class="level-filter error active" data-level="error">ERROR</div>
        </div>
      </div>

      <!-- 内容区域 -->
      <div class="content">
        <!-- Console面板 -->
        <div class="content-panel active" id="console-panel">
          <div class="empty-state" id="console-empty">No console logs yet...</div>
          <div id="console-content"></div>
        </div>

        <!-- Network面板 -->
        <div class="content-panel" id="network-panel">
          <div class="empty-state" id="network-empty">No network requests yet...</div>
          <div id="network-content"></div>
        </div>
      </div>

      <!-- 统计信息 -->
      <div class="stats" id="stats">Logs: <span id="log-count">0</span> | Requests: <span id="request-count">0</span> | Queue: <span id="queue-count">0</span></div>
    </div>

    <script>
      class DebugPanel {
        constructor() {
          this.logs = []
          this.networkLogs = []
          this.activeTab = 'console'
          this.activeFilters = new Set(['debug', 'info', 'log', 'warn', 'error'])
          this.searchTerm = ''
          this.autoScroll = true

          this.initWebSocket()
          this.initEventListeners()
          this.updateStats()
        }

        initWebSocket() {
          const wsProtocol = window.location.protocol === 'https:' ? 'wss://' : 'ws://'
          const wsHost = window.location.hostname
          const wsPort = 3000

          this.ws = new WebSocket(`${wsProtocol}${wsHost}:${wsPort}`)

          this.ws.onopen = () => {
            document.getElementById('status_indicator').className = 'status-indicator connected'
            document.getElementById('status').textContent = 'Connected'
          }

          this.ws.onclose = () => {
            document.getElementById('status_indicator').className = 'status-indicator disconnected'
            document.getElementById('status').textContent = 'Disconnected'

            // 尝试重连
            setTimeout(() => this.initWebSocket(), 3000)
          }

          this.ws.onmessage = (event) => {
            try {
              const data = JSON.parse(event.data)
              this.handleMessage(data)
            } catch (e) {
              // 兼容旧格式
              this.handleLegacyMessage(event.data)
            }
          }

          this.ws.onerror = (error) => {
            console.error('WebSocket Error:', error)
          }
        }

        handleMessage(data) {
          if (data.type === 'log') {
            this.addLog(data.data)
          } else if (data.type === 'network') {
            this.addNetworkLog(data.data)
          } else if (data.type === 'clear') {
            this.clearLogs()
          }
        }

        handleLegacyMessage(message) {
          // 处理旧格式的消息
          const lines = message.split('\n')
          lines.forEach((line) => {
            if (line.trim()) {
              this.addLog({
                id: Date.now() + Math.random(),
                timestamp: Date.now().toString(),
                level: 'log',
                message: [line],
              })
            }
          })
        }

        addLog(logData) {
          this.logs.push(logData)
          if (this.logs.length > 1000) {
            this.logs = this.logs.slice(-1000)
          }

          if (this.activeTab === 'console') {
            this.renderLogs()
          }
          this.updateStats()
        }

        addNetworkLog(networkData) {
          this.networkLogs.push(networkData)
          if (this.networkLogs.length > 500) {
            this.networkLogs = this.networkLogs.slice(-500)
          }

          if (this.activeTab === 'network') {
            this.renderNetworkLogs()
          }
          this.updateStats()
        }

        renderLogs() {
          const container = document.getElementById('console-content')
          const emptyState = document.getElementById('console-empty')

          const filteredLogs = this.logs.filter((log) => this.activeFilters.has(log.level) && (this.searchTerm === '' || log.message.some((msg) => String(msg).toLowerCase().includes(this.searchTerm.toLowerCase()))))

          if (filteredLogs.length === 0) {
            container.innerHTML = ''
            emptyState.style.display = 'block'
            return
          }

          emptyState.style.display = 'none'

          container.innerHTML = filteredLogs
            .map((log) => {
              const timestamp = new Date(parseInt(log.timestamp)).toLocaleTimeString()
              const messages = log.message.map((msg) => (typeof msg === 'object' ? JSON.stringify(msg, null, 2) : String(msg))).join(' ')

              return `
              <div class="log-entry ${log.level}">
                <span class="log-timestamp">${timestamp}</span>
                <span class="log-level">${log.level}</span>
                <span class="log-message">${this.escapeHtml(messages)}</span>
              </div>
            `
            })
            .join('')

          if (this.autoScroll) {
            container.scrollTop = container.scrollHeight
          }
        }

        renderNetworkLogs() {
          const container = document.getElementById('network-content')
          const emptyState = document.getElementById('network-empty')

          const filteredLogs = this.networkLogs.filter((log) => this.searchTerm === '' || log.url.toLowerCase().includes(this.searchTerm.toLowerCase()) || log.method.toLowerCase().includes(this.searchTerm.toLowerCase()))

          if (filteredLogs.length === 0) {
            container.innerHTML = ''
            emptyState.style.display = 'block'
            return
          }

          emptyState.style.display = 'none'

          container.innerHTML = filteredLogs
            .map((log) => {
              const timestamp = new Date(parseInt(log.startTime)).toLocaleTimeString()
              const statusClass = log.success ? 'success' : 'error'

              return `
              <div class="network-entry" onclick="toggleNetworkDetails('${log.id}')">
                <div class="network-header">
                  <span class="network-method ${log.method}">${log.method}</span>
                  <span class="network-url" title="${log.url}">${log.url}</span>
                  <span class="network-status ${statusClass}">${log.status}</span>
                  <span class="network-duration">${log.duration}ms</span>
                </div>
                <div class="network-details" id="details-${log.id}">
                  <div class="network-section">
                    <div class="network-section-title">Request Headers</div>
                    <div class="network-section-content">${this.formatJson(log.headers)}</div>
                  </div>
                  ${
                    log.body
                      ? `
                    <div class="network-section">
                      <div class="network-section-title">Request Body</div>
                      <div class="network-section-content">${this.formatRequestBody(log.body)}</div>
                    </div>
                  `
                      : ''
                  }
                  <div class="network-section">
                    <div class="network-section-title">Response Headers</div>
                    <div class="network-section-content">${this.formatJson(log.responseHeaders)}</div>
                  </div>
                  <div class="network-section">
                    <div class="network-section-title">Response Body</div>
                    <div class="network-section-content">${this.formatJson(log.responseData)}</div>
                  </div>
                </div>
              </div>
            `
            })
            .join('')

          if (this.autoScroll) {
            container.scrollTop = container.scrollHeight
          }
        }

        formatJson(data) {
          try {
            return JSON.stringify(data, null, 2)
          } catch (e) {
            return String(data)
          }
        }

        formatRequestBody(body) {
          if (typeof body === 'string') {
            try {
              return JSON.stringify(JSON.parse(body), null, 2)
            } catch (e) {
              return body
            }
          }
          return this.formatJson(body)
        }

        escapeHtml(text) {
          const div = document.createElement('div')
          div.textContent = text
          return div.innerHTML
        }

        initEventListeners() {
          // Tab切换
          document.querySelectorAll('.tab').forEach((tab) => {
            tab.addEventListener('click', (e) => {
              const tabName = e.target.dataset.tab
              this.switchTab(tabName)
            })
          })

          // 筛选器
          document.querySelectorAll('.level-filter').forEach((filter) => {
            filter.addEventListener('click', (e) => {
              const level = e.target.dataset.level
              this.toggleFilter(level)
            })
          })

          // 搜索
          document.getElementById('searchInput').addEventListener('input', (e) => {
            this.searchTerm = e.target.value
            this.renderCurrentTab()
          })

          // 自动滚动
          document.getElementById('autoScroll').addEventListener('change', (e) => {
            this.autoScroll = e.target.checked
          })

          // 清空按钮
          document.getElementById('clearBtn').addEventListener('click', () => {
            this.clearLogs()
          })

          // 导出按钮
          document.getElementById('exportBtn').addEventListener('click', () => {
            this.exportLogs()
          })
        }

        switchTab(tabName) {
          // 更新tab样式
          document.querySelectorAll('.tab').forEach((tab) => {
            tab.classList.remove('active')
          })
          document.querySelector(`[data-tab="${tabName}"]`).classList.add('active')

          // 更新内容面板
          document.querySelectorAll('.content-panel').forEach((panel) => {
            panel.classList.remove('active')
          })
          document.getElementById(`${tabName}-panel`).classList.add('active')

          this.activeTab = tabName
          this.renderCurrentTab()
        }

        toggleFilter(level) {
          const filterEl = document.querySelector(`[data-level="${level}"]`)

          if (this.activeFilters.has(level)) {
            this.activeFilters.delete(level)
            filterEl.classList.remove('active')
          } else {
            this.activeFilters.add(level)
            filterEl.classList.add('active')
          }

          if (this.activeTab === 'console') {
            this.renderLogs()
          }
        }

        renderCurrentTab() {
          if (this.activeTab === 'console') {
            this.renderLogs()
          } else if (this.activeTab === 'network') {
            this.renderNetworkLogs()
          }
        }

        clearLogs() {
          this.logs = []
          this.networkLogs = []
          this.renderCurrentTab()
          this.updateStats()
        }

        exportLogs() {
          const data = {
            logs: this.logs,
            networkLogs: this.networkLogs,
            exportTime: new Date().toISOString(),
          }

          const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' })
          const url = URL.createObjectURL(blob)
          const a = document.createElement('a')
          a.href = url
          a.download = `debug-logs-${Date.now()}.json`
          a.click()
          URL.revokeObjectURL(url)
        }

        updateStats() {
          document.getElementById('log-count').textContent = this.logs.length
          document.getElementById('request-count').textContent = this.networkLogs.length
          document.getElementById('queue-count').textContent = '0'
        }
      }

      function toggleNetworkDetails(id) {
        const details = document.getElementById(`details-${id}`)
        details.classList.toggle('show')
      }

      // 初始化调试面板
      const debugPanel = new DebugPanel()
    </script>
  </body>
</html>


